// Package service @author dingrui @since 2021/11/26
package service

import (
	"fmt"
	"log"
	"sync"

	"github.com/gomodule/redigo/redis"

	"github.com/bannirui/mini-prize/common"
	"github.com/bannirui/mini-prize/dal/dao"
	"github.com/bannirui/mini-prize/dal/model"
	"github.com/bannirui/mini-prize/datasource"
)

var (
	// 单例 暴露接口
	G_blackIpService BlackIpService
	// IP信息缓存
	cachedBlackIpList = make(map[string]*model.BlackIp)
	cachedBlackIpLock = sync.Mutex{}
)

const (
	// 缓存key的前缀
	InfoBlackIpCacheKeyPrefix = "info_black_ip_"
)

//
//  BlackIpService
//  @Description:
//
type BlackIpService interface {
	GetAll(page, size int) []model.BlackIp
	CountAll() int64
	Search(ip string) []model.BlackIp
	Get(id int) *model.BlackIp
	Update(user *model.BlackIp, columns []string) error
	Create(user *model.BlackIp) error
	GetByIp(ip string) *model.BlackIp
}

//
//  blackIpService
//  @Description: 实现抽象接口
//
type blackIpService struct {
	dao *dao.BlackIpDao
}

func init() {
	// 单例赋值 对象向上转接口类型
	G_blackIpService = &blackIpService{dao: dao.G_blackIpDao}
}

func (s *blackIpService) GetAll(page, size int) []model.BlackIp {
	return s.dao.PageAll(page, size)
}

func (s *blackIpService) CountAll() int64 {
	return s.dao.CountAll()
}

func (s *blackIpService) Search(ip string) []model.BlackIp {
	return s.dao.ListByIp(ip)
}

func (s *blackIpService) Get(id int) *model.BlackIp {
	return s.dao.GetById(id)
}

func (s *blackIpService) Update(data *model.BlackIp, columns []string) error {
	// 先更新缓存的数据
	s.updateByCache(data, columns)
	// 再更新数据的数据
	return s.dao.Update(data, columns)
}

func (s *blackIpService) Create(data *model.BlackIp) error {
	return s.dao.InsertOne(data)
}

// 根据IP读取IP的黑名单数据
func (s *blackIpService) GetByIp(ip string) *model.BlackIp {
	// 先从缓存中读取数据
	data := s.getByCache(ip)
	if data == nil || data.Ip == "" {
		// 再从数据库中读取数据
		data = s.dao.GetByIp(ip)
		if data == nil || data.Ip == "" {
			data = &model.BlackIp{Ip: ip}
		}
		s.setByCache(data)
	}
	return data
}

func (s *blackIpService) getByCache(ip string) *model.BlackIp {
	var (
		key string
	)
	// 集群模式，redis缓存
	key = fmt.Sprintf("info_blackip_%s", ip)
	dataMap, err := redis.StringMap(datasource.G_redis.Do("HGETALL", key))
	if err != nil {
		log.Println("blackip_service.getByCache HGETALL key=", key, ", error=", err)
		return nil
	}
	dataIp := common.GetStringFromStringMap(dataMap, "Ip", "")
	if dataIp == "" {
		return nil
	}
	data := &model.BlackIp{
		Id:         int(common.GetInt64FromStringMap(dataMap, "Id", 0)),
		Ip:         dataIp,
		ExpireTime: int(common.GetInt64FromStringMap(dataMap, "ExpireTime", 0)),
		GmtCreated: int(common.GetInt64FromStringMap(dataMap, "GmtCreated", 0)),
		GmtUpdated: int(common.GetInt64FromStringMap(dataMap, "GmtUpdated", 0)),
	}
	return data
}

func (s *blackIpService) setByCache(data *model.BlackIp) {
	if data == nil || data.Ip == "" {
		return
	}
	// 集群模式，redis缓存
	key := fmt.Sprintf("info_blackip_%s", data.Ip)
	// 数据更新到redis缓存
	params := []interface{}{key}
	params = append(params, "Ip", data.Ip)
	if data.Id > 0 {
		params = append(params, "ExpireTime", data.ExpireTime)
		params = append(params, "GmtCreated", data.GmtCreated)
		params = append(params, "GmtUpdated", data.GmtUpdated)
	}
	_, err := datasource.G_redis.Do("HMSET", params...)
	if err != nil {
		log.Println("blackip_service.setByCache HMSET params=", params, ", error=", err)
	}
}

// 数据更新了，直接清空缓存数据
func (s *blackIpService) updateByCache(data *model.BlackIp, columns []string) {
	var (
		key string
	)
	if data == nil || data.Ip == "" {
		return
	}
	// 集群模式，redis缓存
	key = fmt.Sprintf("info_blackip_%s", data.Ip)
	// 删除redis中的缓存
	_, _ = datasource.G_redis.Do("DEL", key)
}
